package com.hup.utils.swing;

import com.hup.utils.commons.hardware.MouseUtil;
import com.hup.utils.commons.interfaces.BaseConstant;

import javax.swing.DefaultComboBoxModel;
import javax.swing.InputMap;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.MenuElement;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
import javax.swing.text.NumberFormatter;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.TrayIcon;
import java.awt.Window;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Objects;

/**
 * @author hugan
 * @date 2019/12/9
 */
public class ComponentUtil {

    /**
     * 最小化窗口
     */
    public static void minFrame(JFrame frame) {
        frame.toBack();//解决[尽管最小化,还是会使系统任务栏置顶]
        frame.setState(JFrame.ICONIFIED);
    }

    /**
     * 使最小化的窗口(仅{@link #minFrame(JFrame)})还原和前置
     */
    public static void restoreFrame(JFrame frame) {
        frame.setState(JFrame.NORMAL);
        frame.toFront();
    }

    /**
     * 将菜单和组件绑定,并添加监听
     */
    public static void addMenu(JComponent parent, JPopupMenu menu, ActionListener listener) {
        parent.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (MouseUtil.isRightButton(e)) {
                    menu.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        });
        for (MenuElement item : menu.getSubElements()) {
            ((JMenuItem) item.getComponent()).addActionListener(listener);
        }
    }

    /**
     * 将菜单和组件绑定,并添加监听
     */
    public static void addMenu(TrayIcon trayIcon, JPopupMenu menu, ActionListener listener) {
        trayIcon.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (MouseUtil.isRightButton(e)) {
                    //直接使用show方法,会导致,鼠标在item上移动时没有聚焦效果!!原因未知
                    //menu.show(e.getComponent(), e.getX(), e.getY());
                    menu.setLocation(e.getX(), e.getY());
                    menu.setInvoker(menu);
                    menu.setVisible(true);
                }
            }
        });
        for (MenuElement item : menu.getSubElements()) {
            ((JMenuItem) item.getComponent()).addActionListener(listener);
        }
    }

    /**
     * 在容器中查找指定类的子元素(一般调用时是明确知道有唯一的子元素的!)
     *
     * @param <T> 例如:FilePane
     * @return nullable
     */
    @SuppressWarnings("unchecked")
    public static <T> T findChild(Container parent, Class<T> clz) {
        for (Component comp : parent.getComponents()) {
            if (clz.isAssignableFrom(comp.getClass())) {
                return (T) comp;
            }
            if (comp instanceof Container) {
                Container cont = (Container) comp;
                if (cont.getComponentCount() > 0) {
                    T found = findChild(cont, clz);
                    if (found != null) {
                        return found;
                    }
                }
            }
        }

        return null;
    }

    /**
     * 根据标签名选中tab;没匹配时,不处理
     */
    public static void selectTabByTitle(JTabbedPane tpContent, String title) {
        for (int i = tpContent.getTabCount() - 1; i >= 0; i--) {
            if (Objects.equals(tpContent.getTitleAt(i), title)) {
                tpContent.setSelectedIndex(i);
            }
        }
    }

    public static String getSelectedTabTitle(JTabbedPane tp) {
        return tp.getTitleAt(tp.getSelectedIndex());
    }

    /**
     * 当表格排序后,getSelectedRow()获取到的index是'错误的',需要转换
     */
    public static int getSelectedRow(JTable table) {
        return table.convertRowIndexToModel(table.getSelectedRow());
    }

    /**
     * 获取[内置的键盘功能]对象;可用于清除默认功能({@link InputMap#clear()})
     */
    public static InputMap getInputMap(JComponent c) {
        if (c instanceof JTable) {
            return c.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).getParent();
        } else {
            return c.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        }
    }

    @SuppressWarnings("unchecked")
    public static void setItem(JComboBox comboBox, Class<? extends Enum<? extends BaseConstant>> clz) {
        comboBox.setModel(new DefaultComboBoxModel(BaseConstant.getSelectItem(clz)));
    }

    /**
     * 追加文本,且滚动到末尾(如果有选中文本,则不滚动)
     */
    public static void appendAndScroll(JTextArea c, String str) {
        c.append(str);
        int st = c.getSelectionStart();
        int ed = c.getSelectionEnd();
        if (st == ed) {
            int length = c.getText().length();
            c.setSelectionStart(length);
            c.setSelectionEnd(length);
        }
    }

    /**
     * @param component 某些控件需要setOpaque才能生效背景色
     */
    public static void setBackground(JComponent component, Color color) {
        component.setOpaque(true);
        component.setBackground(color);
    }

    public static Window getParentWindow(Component component) {
        return SwingUtilities.windowForComponent(component);
    }

    /**
     * 使滚动窗滚到[顶部/底部,左边/右边]
     *
     * @param isTop nullable
     */
    public static void scrollPaneTo(JScrollPane sp, Boolean isTop, Boolean isLeft) {
        SwingUtilities.invokeLater(() -> {
            //更新子控件后,ScrllBar的尺寸也不是马上能刷新的,所以都要在invokeLater中获取
            Integer x = null;
            Integer y = null;
            if (isLeft != null) {
                JScrollBar sb = sp.getHorizontalScrollBar();
                x = isLeft ? 0 : sb.getMaximum();
            }
            if (isTop != null) {
                JScrollBar sb = sp.getVerticalScrollBar();
                y = isTop ? 0 : sb.getMaximum();
            }
            scrollPaneTo(sp, x, y);
        });
    }

    /**
     * 使滚动窗滚到[子组件目标处]
     *
     * @param child 需要在sp中,不在也不会抛异常
     */
    public static void scrollPaneTo(JScrollPane sp, Component child) {
        SwingUtilities.invokeLater(() -> scrollPaneTo(sp, child.getX(), child.getY()));
    }

    /**
     * 使滚动窗滚到[目标位置];
     *
     * @apiNote 使用调用者线程更新ui;如果在非ui线程中,更新sp子控件尺寸后需要定位,则不应该调用此方法!
     * (即使是ui线程貌似也不应该调用? invokeLater是最没问题的?)
     */
    public static void scrollPaneTo(JScrollPane sp, Integer x, Integer y) {
        if (x != null) sp.getHorizontalScrollBar().setValue(x);
        if (y != null) sp.getVerticalScrollBar().setValue(y);
    }

    /**
     * 如JPanel增加或删除了子控件后,需要马上刷新界面时调用;
     */
    public static <T extends Container> void afterChangedChild(T c) {
        c.revalidate();
        c.repaint();
    }

    /**
     * 全选输入栏的文本
     */
    public static void selectAllText(JTextComponent c) {
        int length = c.getDocument().getLength();
        if (length > 0) c.select(0, length);
    }

    /**
     * 获取限制输入的文本框
     */
    public enum FormatTextType {
        /**
         * 正数:>=0的double
         */
        DOUBLE_GEQ0;

        public static JFormattedTextField getTxt(FormatTextType type) {
            AbstractFormatter formatter = null;
            switch (type) {
                case DOUBLE_GEQ0:
                    NumberFormatter f = new NumberFormatter();
                    f.setMinimum(0d);
                    formatter = f;
                    break;
            }
            return new JFormattedTextField(formatter);
        }
    }

}
